﻿
/**
 * Constructs a <Dictionary>, a quasi- associative object that supports linq-like labmda operations
 * Especially necessary because JQuery does not allow extending the <Object> object
 *
 * @constructor
 * @this {Dictionary}
 * @param {Aarray,Object} source An initial array or plain object to seed the disctionary with.
 */

function Dictionary(source)
{
    var _data = {};
    
    if (source && typeof (source) == 'object') // works whether source is just an object or is also an array
    {
        for (var key in source)
            if (source.hasOwnProperty(key))
                _data[key] = source[key];
    }


    this.hasKey = function(key)
    {
        return _data.hasOwnProperty(key);
    }
    this.get = function(key)
    {
        return _data[key];
    }
    this.put = function (key, value)
    {
        _data[key] = value;
    }
    this.remove = function (key)
    {
        delete _data[key];
    }

    this.keys = function ()
    {
        var _keys = [];
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                _keys.push(key);
        return _keys;
    }
    this.values = function ()
    {
        var _values = [];
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                _values.push(_data[key]);
        return _values;
    }
    this.entries = function ()
    {
        var _entries = [];
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                _entries.push({ key: key, value: _data[key] });
        return _entries;
    }

    this.count = function ()
    {
        return this.keys().length;
    }
    this.select = function (func)
    {
        var dictionary = new Dictionary();
        var index = 0;
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                dictionary.put(key, func ? func(_data[key], key, index++) : _data[key]);
        return dictionary;
    }
    this.where = function (func)
    {
        var dictionary = new Dictionary();
        var index = 0;
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                if (!func || func(_data[key], key, index++))
                    dictionary.put(key, _data[key]);
        return dictionary;
    }
    this.accumulate = function (initial, func) // initial parameter is required
    {
        var accum = initial;
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                accum = func ? func(accum, _data[key]) : (accum + _data[key]);
        return accum;
    }
    this.forAll = function (func)
    {
        var index = 0;
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                if (func)
                    func(_data[key], key, index++);
    }

    this.sum = function (func)
    {
        var sum = 0;
        var index = 0;
        for (var key in _data)
            if (_data.hasOwnProperty(key)) {
                //alert('key: '+key);
                sum += (func ? func(_data[key], key, index++) : _data[key]);
            }
        return sum;
    }
    this.avg = function (func)
    {
        return this.sum(func) / this.count();
    }
    this.max = function (func)
    {
        var max = Number.NEGATIVE_INFINITY;
        var index = 0;
        for (var key in _data)
            if (_data.hasOwnProperty(key))
            {
                var value = (func ? func(_data[key], key, index++) : _data[key]);
                if (value > max)
                    max = value;
            }
        return max;
    }
    this.min = function (func)
    {
        var min = Number.POSITIVE_INFINITY;
        var index = 0;
        for (var key in _data)
            if (_data.hasOwnProperty(key))
            {
                var value = (func ? func(_data[key], key, index++) : _data[key]);
                if (value < max)
                    min = value;
            }
        return min;
    }

    this.orderKeysBy = function(func)
    {
        var dictionary = new Dictionary();
        var orderedKeys = this.keys().orderBy(func);
        for (var keyIndex in orderedKeys)
            if (orderedKeys.hasOwnProperty(keyIndex))
                dictionary.put(orderedKeys[keyIndex], _data[orderedKeys[keyIndex]]);
        return dictionary;
    }
    this.orderKeysByDesc = function (func)
    {
        var dictionary = new Dictionary();
        var orderedKeys = this.keys()
            .orderBy(func)
            .reverse();
        for (var keyIndex in orderedKeys)
            if (orderedKeys.hasOwnProperty(keyIndex))
                dictionary.put(orderedKeys[keyIndex], _data[orderedKeys[keyIndex]]);
        return dictionary;
    }
    this.orderValuesBy = function (func)
    {
        var dictionary = new Dictionary();
        var orderedKeys = this.keys().orderBy(function(key) { return func ? func(_data[key]) : _data[key]; });
        for (var keyIndex in orderedKeys)
            if (orderedKeys.hasOwnProperty(keyIndex))
                dictionary.put(orderedKeys[keyIndex], _data[orderedKeys[keyIndex]]);
        return dictionary;
    }
    this.orderValuesByDesc = function (func)
    {
        var dictionary = new Dictionary();
        var orderedKeys = this.keys()
            .orderBy(function (key) { return func ? func(_data[key]) : _data[key]; })
            .reverse();
        for (var keyIndex in orderedKeys)
            if (orderedKeys.hasOwnProperty(keyIndex))
                dictionary.put(orderedKeys[keyIndex], _data[orderedKeys[keyIndex]]);
        return dictionary;
    }
    this.take = function (count)
    {
        var dictionary = new Dictionary();
        var counter = 0;
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                if (++counter <= count)
                    dictionary.put(key, _data[key]);
        return dictionary;
    }
    this.skip = function (count)
    {
        var dictionary = new Dictionary();
        var counter = 0;
        for (var key in _data)
            if (_data.hasOwnProperty(key))
                if (++counter > count)
                    dictionary.put(key, _data[key]);
        return dictionary;
    }
}
